#ifndef DEBUG_UTILS_H
#define DEBUG_UTILS_H

#include <stdio.h>
#include <stdlib.h>

/**
 * ANSI color codes
 */
#ifdef USE_COLOR

#define COLOR_RED       "\033[0;31m"
#define COLOR_GREEN     "\033[0;32m"
#define COLOUR_RESET    "\033[0m"

#else

#define COLOR_RED       ""
#define COLOR_GREEN     ""
#define COLOUR_RESET    ""

#endif

/** 
 * Debug macros
 */
#ifdef DEBUG

#define DEBUG_PRINT(fmt, ...) \
        do { \
                fprintf(stderr, COLOR_RED "DEBUG: %s:%d:%s(): " fmt \
                        COLOUR_RESET "\n", \
                        __FILE__, __LINE__, __func__, ##__VA_ARGS__); \
        } while (0)

/** 
 * Assertion macro
 */
#define ASSERT(cond) \
        do { \
                if (!(cond)) { \
                        fprintf(stderr, COLOR_RED "ASSERTION FAILED: %s:%d:%s(): " \
                                "%s\n" COLOUR_RESET, \
                                __FILE__, __LINE__, __func__, #cond); \
                        abort(); \
                } \
        } while (0)

/** 
 * Null pointer check macro
 */
#define CHECK_NULL(ptr) \
        do { \
                if ((ptr) == NULL) { \
                        fprintf(stderr, COLOR_RED "NULL POINTER ERROR: %s:%d:%s(): " \
                                "%s is NULL\n" COLOUR_RESET, \
                                __FILE__, __LINE__, __func__, #ptr); \
                        abort(); \
                } \
        } while (0)

/** 
 * Print variable value macro
 */
#define PRINT_VAR(var) \
        do { \
                fprintf(stderr, COLOR_RED "DEBUG: %s:%d:%s(): " #var " = %d\n" \
                        COLOUR_RESET, \
                        __FILE__, __LINE__, __func__, (var)); \
        } while (0)

/** 
 * Print string macro
 */
#define PRINT_STR(str) \
        do { \
                fprintf(stderr, COLOR_RED "DEBUG: %s:%d:%s(): %s = \"%s\"\n" \
                        COLOUR_RESET, \
                        __FILE__, __LINE__, __func__, #str, (str)); \
        } while (0)

/** 
 * Safe free macro
 */
#define SAFE_FREE(ptr) \
        do { \
                if ((ptr) != NULL) { \
                        free((ptr)); \
                        (ptr) = NULL; \
                } \
        } while (0)

/** 
 * Print struct macro
 */
#define PRINT_STRUCT(s) \
        do { \
                fprintf(stderr, COLOR_RED "DEBUG: %s:%d:%s(): %s\n" \
                        COLOUR_RESET, \
                        __FILE__, __LINE__, __func__, #s); \
                PRINT_STRUCT_FIELDS(s); \
        } while (0)

/**
 * Macro to define specific fields printing for structs
 */
#define PRINT_STRUCT_FIELDS(s) \
        do { \
                fprintf(stderr, COLOR_GREEN); \
                fprintf(stderr, "    id: %s\n", (s).id); \
                fprintf(stderr, "    name: %s\n", (s).name); \
                fprintf(stderr, "    gender: %d\n", (s).gender); \
                fprintf(stderr, "    date of birth: %s\n", (s).dob); \
                fprintf(stderr, COLOUR_RESET); \
        } while (0)

#else

#define DEBUG_PRINT(fmt, ...) do {} while (0)
#define ASSERT(cond) do {} while (0)
#define CHECK_NULL(ptr) do {} while (0)
#define PRINT_VAR(var) do {} while (0)
#define PRINT_STR(str) do {} while (0)
#define PRINT_STRUCT(s) do {} while (0)
#define PRINT_STRUCT_FIELDS(s) do {} while (0)
#endif // DEBUG

#endif // DEBUG_UTILS_H
